package view.game2048;

import java.io.File;
import java.util.Observable;
import java.util.Timer;
import java.util.TimerTask;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import view.View;
/**
 * The Game2048View class is one layer out of three of the MVP design pattern.This layer (the view layer) 
 * holds the implementation of the GUI and interacts directly with the user.
 * 
 * @author Peled Eldan
 * @author Daniel Manor
 */
public class Game2048View extends Observable implements View, Runnable
{
	// Data Members
	private Display display;
	private Shell shell;
	private Game2048Board board;
	private Button startReset;
	private Button undo;
	private Button mute;
	private Button hint;
	private int score;
	private Text scoreText;
	private Text scoreTitle;
	private int userCommand;
	private Game2048Menu menu;
	private boolean autoRun=true,isStarted=false,wiki=true;
	
	
	private Timer t = new Timer();
	
	TimerTask tt; 

	// Statics
	public static final int GAME_OVER = -999;
	public static final int EMPTY = 0;
	public static final int ACTION_ARROW_UP = 0;
	public static final int ACTION_ARROW_DOWN = 1;
	public static final int ACTION_ARROW_LEFT = 2;
	public static final int ACTION_ARROW_RIGHT = 3;
	public static final int ACTION_START_RESET = 4;
	public static final int ACTION_UNDO = 5;
	public static final int ACTION_SAVE = 6;
	public static final int ACTION_LOAD = 7;
	public static final int ACTION_MUTE = 16;
	public static final int ACTION_HINT = 100;
	public static final int AUTO_RUN = 101;
	public static final int CHANGE_SONG = 666;

	// Game2048View EMPTY CTOR
	public Game2048View()	{}

	private void initComponents()
	{
		display = new Display();
		shell = new Shell(display);
		shell.setLayout(new GridLayout(2, false));
		shell.setSize(400,350);
		shell.setText("2048 Game");

		// Create the menuBar
		this.menu = new Game2048Menu(this, shell);		

		// Create the Start / Restart button
		startReset = new Button(shell, SWT.PUSH);
		startReset.setText("Start / Restart");
		startReset.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false, 1, 1));

		// Create the board
		this.board = new Game2048Board(shell, SWT.BORDER);
		board.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 5));

		// Create the Undo button
		undo = new Button(shell, SWT.PUSH);
		undo.setText("Undo");
		undo.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false, 1, 1));
		
		// Create the Hint button
		hint = new Button(shell, SWT.PUSH);
		hint.setText("Hint");
		hint.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false, 1, 1));

		// Create a Separator Label
		Label separatorButtons = new Label(shell, SWT.SEPARATOR | SWT.SHADOW_OUT | SWT.HORIZONTAL);
		separatorButtons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1));

		// Create the Mute Checkbox button
		mute = new Button(shell, SWT.CHECK);
		mute.setText("Mute Music");
		mute.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false, 1, 1));

		// Create the scoreTitle text
		scoreTitle = new Text(shell,SWT.SINGLE);
		scoreTitle.setText("Score:");
		scoreTitle.setBackground(this.shell.getBackground());
		scoreTitle.setFont(new Font(display, new FontData("Tahoma", shell.getClientArea().height * 5 / 100, SWT.BOLD)));
		scoreTitle.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, false, true, 1, 1));

		// Create the scoreText text
		scoreText = new Text(shell, SWT.SINGLE);
		scoreText.setText(Integer.toString(score));
		scoreText.setBackground(this.shell.getBackground());
		scoreText.setFont(new Font(display, new FontData("Tahoma", shell.getClientArea().height * 5 / 100, SWT.NORMAL)));
		scoreText.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, false, false, 1, 1));	

		// Add listeners and filters
		addListeners();
		addFilters();

		// Set the menu bar to the shell
		shell.setMenuBar(menu.getMenuBar());

		// Set the mouse & keyboard focus to the board
		this.board.setFocus();

		// Open the shell window
		shell.open();
	}

	// Adds the appropriate listeners to the buttons
	private void addListeners() 
	{
		shell.addListener(SWT.Close, new Listener() {
			
			@Override
			public void handleEvent(Event e) {
				
				if(!autoRun)
				{
					tt.cancel();
		        	t.cancel();
		    	   autoRun=true;
				}
				System.exit(0);
			}
		});
		// If startReset button is clicked, setUserCommand to Game2048View.ACTION_START_RESET
		startReset.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetSelected(SelectionEvent e) 
			{
				isStarted = true;
				setUserCommand(Game2048View.ACTION_START_RESET);
				board.setFocus();
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e)
			{ /* DO NOTHING */ }

		});

		undo.addSelectionListener(new SelectionListener() {

			@Override
			public void widgetSelected(SelectionEvent e) 
			{
				setUserCommand(Game2048View.ACTION_UNDO);
				board.setFocus();
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) 
			{ /* DO NOTHING */ }
		});
		//hint
		hint.addSelectionListener(new SelectionListener() {
			
		@Override
		public void widgetSelected(SelectionEvent e) {
			setUserCommand(Game2048View.ACTION_HINT);
			board.setFocus();
				
		}
			@Override
		public void widgetDefaultSelected(SelectionEvent arg0) {
		{ /* DO NOTHING */ }
				
		}
	});
		//Mute
		mute.addSelectionListener(new SelectionListener() {

			@Override
			public void widgetSelected(SelectionEvent e) 
			{
				setUserCommand(Game2048View.ACTION_MUTE);
				board.setFocus();
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) 
			{  /* DO NOTHING */  }
		});
	}

	// Adds the appropriate filters to the display
	// These filters will be used to capture keyboard and mouse actions
	private void addFilters() 
	{
		display.addFilter(SWT.KeyUp, new Listener()
		{	
			@Override
			public void handleEvent(Event event) 
			{
				if ((event.widget instanceof Control) && (isChild(board, (Control)event.widget)))
				{
					if (event.keyCode == SWT.ARROW_UP)
						setUserCommand(Game2048View.ACTION_ARROW_UP);
					else if (event.keyCode == SWT.ARROW_DOWN)
						setUserCommand(Game2048View.ACTION_ARROW_DOWN);
					else if (event.keyCode == SWT.ARROW_LEFT)
						setUserCommand(Game2048View.ACTION_ARROW_LEFT);
					else if (event.keyCode == SWT.ARROW_RIGHT)
						setUserCommand(Game2048View.ACTION_ARROW_RIGHT);
					else if (event.keyCode == SWT.KEYPAD_0)
						setUserCommand(Game2048View.ACTION_HINT);
					else if (event.keyCode == SWT.KEYPAD_9)
							if(isStarted){AutoRun();
							}
						
				}
			}

			// Checks if the child is indeed the parent's child
			private boolean isChild(Control parent, Control child) 
			{
				if (child.equals(parent))
					return true;

				Composite p = child.getParent();

				if (p != null)
					return isChild(parent,p);
				else
					return false;
			}
		});
	}

	@Override
	// setBoardData and then redraw the board
	public void displayData(int[][] data, final int score)
	{
	
		this.board.setBoardData(data);
		this.score = score;

		display.syncExec(new Runnable() {
			@Override
			public void run()
			{
				board.redraw();
				scoreText.setText(Integer.toString(score));				
			}
		});
	}

	@Override
	// Return the userCommand integer
	public int getUserCommand() 
	{
		return userCommand;
	}

	// Update the local command integer
	public void setUserCommand(int cmd)
	{
	
		// Update the local command variable
		this.userCommand = cmd;

		// Notify Observers
		setChanged();
		notifyObservers();
	}

	@Override
	// Run the initComponents method and loop over the display.readAndDispath method
	public void run() 
	{
		initComponents();

		while (!shell.isDisposed())
		{
			if (!display.readAndDispatch())
			{
				display.sleep();
			}
		}
		display.dispose();
	}

	@Override
	// Sets the score
	public void setScore(int score)
	{
		this.score = score;
	}

	@Override
	// Returns the score
	public int getScore(int score)
	{
		return this.score;
	}

	protected Game2048Board getBoard()
	{
		return this.board;
	}

	public void gameOver()
	{
		
	}
    private void AutoRun() 
     {
       if(autoRun){
    	   this.setUserCommand(CHANGE_SONG);
    	   this.autoRun=false;
    	   t=new Timer();
    	  tt = new TimerTask() {
    			
    			@Override
    			public void run() {
    				setUserCommand(AUTO_RUN);
    				 
    				
    			}
    		};
    	t.scheduleAtFixedRate(tt, 0, 250);
    	if(wiki){try{Program.launch("http://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning");
    	wiki=false;}catch(Exception e){}}
       }
       else{
    	 
    	   tt.cancel();
        	t.cancel();
    	   this.autoRun=true;
    	   
    	
       }
           
    	
            
          
    		 
    	

    		 
     }
	@Override
	public String getSaveLocation() 
	{
		boolean canWriteToFile = false;
		File saveFile = null;
		String saveFileLocation = null;

		FileDialog fileDialog = new FileDialog(shell, SWT.SAVE);
		fileDialog.setText("Save Game");
		fileDialog.setFilterExtensions(new String[] {"*.txt"});


		while (!canWriteToFile)
		{
			saveFileLocation = fileDialog.open();

			if (saveFileLocation == null)
				return null;

			saveFile = new File(saveFileLocation);

			if ((saveFile.exists()) && (!saveFile.isDirectory()))
			{
				MessageBox messageBoxOverwrite = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
				messageBoxOverwrite.setText("File Exists");
				messageBoxOverwrite.setMessage("Would you like to overwrite the file selected?");

				int response = messageBoxOverwrite.open();

				if (response == SWT.YES)
				{
					if (saveFile.canWrite())
						canWriteToFile = true;
					else
					{
						MessageBox messageBoxWritePerm = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
						messageBoxWritePerm.setText("Can't Write");
						messageBoxWritePerm.setMessage("The file " + saveFileLocation + " can not be written to. Please check that you have write permissions.");
					}
				}
				else
				{

				}
			}
			else if (saveFile.exists()) // saveFile exists, but is a directory
			{
				MessageBox messageBoxDir = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
				messageBoxDir.setText("Directory Chosen");
				messageBoxDir.setMessage("Can't choose a directory.");
			}
			else
			{
				MessageBox messageBoxCreate = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
				messageBoxCreate.setText("File Does Not Exist");
				messageBoxCreate.setMessage("File does not exist, would you like to create it ?");

				int response = messageBoxCreate.open();

				if (response == SWT.YES)
				{
					canWriteToFile = true;
				}
			}
		}

		if (saveFileLocation.substring(saveFileLocation.length()-4).equals(".txt"))
			return saveFileLocation;
		else
			return saveFileLocation + ".txt";
	}

	@Override
	public String getLoadLocation() 
	{
		boolean canReadFromFile = false;
		File loadFile = null;
		String loadFileLocation = null;

		FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
		fileDialog.setText("Load Game");
		fileDialog.setFilterExtensions(new String[] {"*.txt"});


		while (!canReadFromFile)
		{
			loadFileLocation = fileDialog.open();

			if (loadFileLocation == null)
				return null;

			loadFile = new File(loadFileLocation);

			if (loadFile.exists() && loadFile.isDirectory()) // saveFile exists, but is a directory
			{
				MessageBox messageBoxDir = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
				messageBoxDir.setText("Directory Chosen");
				messageBoxDir.setMessage("Can't choose a directory.");
			}
			else if (!loadFile.exists())
			{
				MessageBox messageBoxCreate = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
				messageBoxCreate.setText("File does not exist");
				messageBoxCreate.setMessage("File " + loadFileLocation + "does not exist. Please choose a different one.");
			}
			else // loadFile.exists() == true
			{
				if (!loadFile.canRead())
				{
					MessageBox messageBoxRead = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
					messageBoxRead.setText("Read Error");
					messageBoxRead.setMessage("File " + loadFileLocation + "can not be read. Please verify that you have read permissions.");
				}
				else
				{
					canReadFromFile = true;
				}
			}
		}

		if (loadFileLocation.substring(loadFileLocation.length()-4).equals(".txt"))
			return loadFileLocation;
		else
			return loadFileLocation + ".txt";
	}

	@Override
	public int[][] getBoardArray() {
		// TODO Auto-generated method stub
		return null;
	}
}
